﻿#region

using System;
using System.Collections.Generic;
using System.Linq;
using Json;
using NUnit.Framework;

#endregion

namespace MobilePal.UnitTests
{
    [TestFixture]
    public class JsonTest
    {
        /// <summary>
        /// Serializes and deserialize collection object.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeCollectionObject()
        {
            var simpleObject = new CollectionObject
                                   {
                                       IntArray = new[] {5, 10},
                                       IntList = new List<int> {6, 7},
                                       IntDictionary = new Dictionary<string, int> {{"a", 6}, {"b", 6}}
                                   };

            var json = JsonMapper.ToJson(simpleObject);

            var secondObject = JsonMapper.ToObject<CollectionObject>(json);

            Assert.AreEqual(secondObject.IntArray.Count(), simpleObject.IntArray.Count());
            Assert.AreEqual(secondObject.IntArray[0], simpleObject.IntArray[0]);
            Assert.AreEqual(secondObject.IntArray[1], simpleObject.IntArray[1]);

            Assert.AreEqual(secondObject.IntList.Count(), simpleObject.IntList.Count());
            Assert.AreEqual(secondObject.IntList[0], simpleObject.IntList[0]);
            Assert.AreEqual(secondObject.IntList[1], simpleObject.IntList[1]);

            Assert.AreEqual(secondObject.IntDictionary.Count(), simpleObject.IntDictionary.Count());
            Assert.AreEqual(secondObject.IntDictionary["a"], simpleObject.IntDictionary["a"]);
            Assert.AreEqual(secondObject.IntDictionary["b"], simpleObject.IntDictionary["b"]);
        }

        /// <summary>
        /// Serializes and deserialize collection with abstract objects.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeCollectionWithAbstractObjects()
        {
            var abstractObjects = new List<AbstractObject> { new BaseObject() };

            var json = JsonMapper.ToJson(abstractObjects);
            var product = JsonMapper.ToObject<List<AbstractObject>>(json);

            Assert.AreEqual(product.Count, 1);
            var item = product[0] as BaseObject;
            Assert.IsNotNull(item);
        }

        /// <summary>
        /// Serializes and deserialize collection with inheritance objects.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeCollectionWithInheritanceObjects()
        {
            var baseObjects = new List<BaseObject> { new ExtendedObject() };

            var json = JsonMapper.ToJson(baseObjects);
            var product = JsonMapper.ToObject<List<BaseObject>>(json);

            Assert.AreEqual(product.Count, 1);
            var item = product[0] as ExtendedObject;
            Assert.IsNotNull(item);
        }

        /// <summary>
        /// Serializes and deserialize complex object.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeComplexObject()
        {
            var simpleObject = new SimpleObject {Value = 5};
            var complexObject = new ComplexObject
                                    {
                                        SimpleObject = simpleObject,
                                        SimpleObjects = new List<SimpleObject> {simpleObject, simpleObject}
                                    };

            var json = JsonMapper.ToJson(complexObject);

            var secondObject = JsonMapper.ToObject<ComplexObject>(json);

            Assert.AreEqual(secondObject.SimpleObject.Value, complexObject.SimpleObject.Value);
            Assert.AreEqual(secondObject.SimpleObjects.Count, 2);
        }

        /// <summary>
        /// Serializes and deserialize object with igore attribute.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeObjectWithIgoreAttribute()
        {
            var ignorableObject = new IgnorableObject {Value = 11};

            var json = JsonMapper.ToJson(ignorableObject);
            var product = JsonMapper.ToObject<IgnorableObject>(json);

            Assert.AreNotEqual(product.Value, ignorableObject.Value);
        }

        /// <summary>
        /// Serializes and deserialize read only object.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeReadOnlyObject()
        {
            var simpleObject = new ReadOnlyObject();

            var json = JsonMapper.ToJson(simpleObject);
            var secondObject = JsonMapper.ToObject<ReadOnlyObject>(json);

            Assert.AreEqual(secondObject.Text, simpleObject.Text);
            Assert.AreEqual(secondObject.Value, simpleObject.Value);
        }

        /// <summary>
        /// Serializes and deserialize same objects.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeSameObjects()
        {
            var simpleObject = new SimpleObject {Value = 5};
            var clone = simpleObject;

            var complexObject = new ComplexObject
                                    {
                                        SimpleObjects = new List<SimpleObject> {simpleObject, clone}
                                    };

            Assert.AreSame(complexObject.SimpleObjects[0], complexObject.SimpleObjects[1]);

            var json = JsonMapper.ToJson(complexObject);
            var secondObject = JsonMapper.ToObject<ComplexObject>(json);

            Assert.AreEqual(secondObject.SimpleObjects.Count, 2);
            Assert.AreSame(secondObject.SimpleObjects[0], secondObject.SimpleObjects[1]);
        }

        /// <summary>
        /// Serializes and deserialize self referenced object.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeSelfReferencedObject()
        {
            var simpleObject = new SelfReferencedObject();
            simpleObject.ReferencedObject = simpleObject;

            var json = JsonMapper.ToJson(simpleObject);
            var secondObject = JsonMapper.ToObject<SelfReferencedObject>(json);

            Assert.AreSame(secondObject.ReferencedObject, secondObject);
        }

        /// <summary>
        /// Serializes and deserialize simple object.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeSimpleObject()
        {
            var simpleObject = new SimpleObject {Value = 5};

            var json = JsonMapper.ToJson(simpleObject);
            var secondObject = JsonMapper.ToObject<SimpleObject>(json);

            Assert.AreEqual(secondObject.Text, simpleObject.Text);
            Assert.AreEqual(secondObject.Value, simpleObject.Value);
            Assert.AreEqual(secondObject.Quality, simpleObject.Quality);
            Assert.AreEqual(secondObject.IsEmpty(), simpleObject.IsEmpty());
        }

        /// <summary>
        /// Serializes the and deserialize date time object.
        /// </summary>
        [Test]
        public void SerializeAndDeserializeDateTimeObject()
        {
            var now = DateTime.Now;
            var simpleObject = new DateTimeObject() { DateTime = now};

            var json = JsonMapper.ToJson(simpleObject);
            var secondObject = JsonMapper.ToObject<DateTimeObject>(json);

            Assert.AreEqual(secondObject.DateTime.Date, simpleObject.DateTime.Date);
            Assert.AreEqual(secondObject.DateTime.Hour, simpleObject.DateTime.Hour);
            Assert.AreEqual(secondObject.DateTime.Minute, simpleObject.DateTime.Minute);
            Assert.AreEqual(secondObject.DateTime.Second, simpleObject.DateTime.Second);
        }
    }

    internal class SimpleObject
    {
        public double Quality = 50.0;
        public string Text;

        public SimpleObject()
        {
            Text = "Hello World";
        }

        public int? Value { get; set; }

        public bool IsEmpty()
        {
            return !Value.HasValue;
        }
    }

    internal class ReadOnlyObject
    {
        public readonly string Text = "Text";

        public int Value
        {
            get { return 5; }
        }
    }

    internal class CollectionObject
    {
        public int[] IntArray;
        public Dictionary<string, int> IntDictionary;
        public List<int> IntList;
    }

    internal class SelfReferencedObject
    {
        public SelfReferencedObject ReferencedObject;
        public int Value;
    }

    internal class ComplexObject
    {
        public SimpleObject SimpleObject;
        public List<SimpleObject> SimpleObjects = new List<SimpleObject>();
    }

    internal class IgnorableObject
    {
        [JsonIgnore] public int Value = 5;
    }

    internal abstract class AbstractObject
    {
        public int Value = 5;

        public virtual string Constructor
        {
            get { return GetType().FullName; }
        }
    }

    internal class BaseObject : AbstractObject
    {
        public int OtherValue = 1;
    }

    internal class ExtendedObject : BaseObject
    {
        public double SomeQuality = 11.14;
    }

    internal class DateTimeObject
    {
        public DateTime DateTime;
    }
}